#!/sbin/sh
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#

#
# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2016 Nexenta Systems, Inc.
#

. /lib/svc/share/fs_include.sh
. /lib/svc/share/net_include.sh

# Make sure that the essential libraries can be found.
LD_LIBRARY_PATH=/lib; export LD_LIBRARY_PATH
STMSBOOTUTIL=/lib/mpxio/stmsboot_util
SAVEDIR=/etc/mpxio
BOOTDEVICES=$SAVEDIR/boot-devices
RECOVERFILE=$SAVEDIR/recover_instructions
DEVFSADM=/usr/sbin/devfsadm
DUMPADM=/usr/sbin/dumpadm
ISROOTDEV=""
ISROOTDEVPATH=""
usrmounted=0
UNAME=/usr/bin/uname
ECHO=/usr/bin/echo
CAT=/usr/bin/cat
CP=/usr/bin/cp
DF=/usr/bin/df
LS=/usr/bin/ls
RM=/usr/bin/rm
EGREP=/usr/bin/egrep
SED=/usr/bin/sed
ZPOOL=/usr/sbin/zpool
AWK=/usr/bin/awk
MOUNT=/sbin/mount
UMOUNT=/sbin/mount
EEPROM=/usr/sbin/eeprom
BOOTADM=/usr/sbin/bootadm
SVCADM=/usr/sbin/svcadm
REBOOT=/usr/sbin/reboot

mpxio_error()
{
	cecho "\nERROR: stmsboot: $1"
	#
	# display recovery instructions - the first call logs to the service
	# log and the second call displays on the console.
	#
	shcat $RECOVERFILE
	shcat $RECOVERFILE >/dev/msglog 2>&1
	cecho "These instructions were also logged to the file $RECOVERFILE\n"
}

#
# root ("/") is already mounted read only by the kernel.
# Remount the root read-write.
#
mpxio_mount_root()
{
	HASZFSROOT=`$DF -g / |grep zfs`
	RVAL=""

	# In single-user maintenance mode, we don't have a writable
	# root partition, so we _cannot_ use devlinks. Therefore we
	# have to do some dancing - first mount the physical path
	# read-write, then re-run $STMSBOOTUTIL to get the real 
	# devlink mapping, and then re-mount the root slice. Of course,
	# if we all used ZFS this wouldn't be such a pain!
	exec < $vfstab; readvfstab /
	# ZFS root environments should _not_ have an entry for /
	# in their /etc/vfstab.
	if [ -n "$special" ]; then
		# sanity check for ZFSRoot _and_ / in /etc/vfstab
		if [ -n "$HASZFSROOT" ]; then
			# ERROR - this would cause a failure later
			# so let root know about it now and provide
			# a chance to handle it before filesystem/usr
			cecho "stmsboot: System has ZFS Root *and* an entry for / in /etc/vfstab\nstmsboot: Please remove the / entry from /etc/vfstab and then run\n# svcadm clear mpxio-upgrade"
			exit 1
		fi
		ISPHYS=`echo $special |$AWK '/^\/dev\/dsk/ {print}'`;
		if [ -z "$ISPHYS" ]; then
			# a metadevice, /dev/vx
			new_special=$special
			$MOUNT -o remount,rw $new_special / >/dev/msglog 2>&1
		else
			new_special=`$STMSBOOTUTIL -m $special`
			if [ "$new_special" = "NOT_MAPPED" ]; then
				# this is a bad state to be in, exit
				cecho "Error: Your root device is not mapped."
				exit 1
			fi
	       		checkopt "llock" $mntopts
			mntopts='remount'
			[ -n "$otherops" ] && mntopts="${mntopts},${otherops}"
			RVAL=`$MOUNT -m -F $fstype -o $mntopts $new_special \
				$mountp >/dev/msglog 2>&1`
			# if we've got active-active paths to our rootvp and
			# the first path returned by $STMSBOOTUTIL is not the
			# same as the one we booted from, then we need some
			# handwaving due to restrictions in the ufs module
			# (see the remountfs() function in 
			# $SRC/uts/common/fs/ufs/ufs_vfsops.c)
			if [ $? -eq 0 ]; then
				# now re-run $STMSBOOTUTIL to get the real
				# mapping for this device
				new_special=`$STMSBOOTUTIL -m $special`
				# mount root for real
				$MOUNT -o remount,rw $new_special / \
				    >/dev/msglog 2>&1
			else
				for device in `$CAT $BOOTDEVICES`; do
					new_special="/devices${device}"
					$MOUNT -m -F $fstype -o $mntopts \
					    $new_special $mountp >/dev/msglog 2>&1
					if [ $? -eq 0 ]; then
						# success, break out
						ISROOTDEVPATH=`$ECHO $device | \
						$AWK -F":" '{print $1}'`
					    break;
					fi
				done
				if [ -n "$RVAL" ]; then
					cecho "Error: Unable to remount your root device"
					exit 1;
				fi
			fi
		fi
	else
		if [ -z "$HASZFSROOT" ]; then
			cecho "stmsboot: Error: your root slice is invalid"
			exit 1
		else
			cecho "stmsboot: Root is on ZFS"
		fi
	fi
}

#
# mount /usr read only
#
mpxio_mount_usr()
{
	exec < $vfstab; readvfstab "/usr"
	ret_val=0
	if [ -n "$mountp" ]; then
		case "$special" in
		/dev/vx/*)
			new_special=$special
			;;
		*)
			new_special=`$STMSBOOTUTIL -m $special`
			;;
		esac
		
		#
		# Must use -o largefiles here to ensure the read-only
		# mount does not fail as a result of having a large
		# file present on /usr.
		#
		if [ "$mntopts" = "-" ]; then
			mntopts='ro,largefiles'
		else
			checkopt largefiles $mntopts
			if [ "$option" != "largefiles" ]; then
				mntopts="largefiles,$mntopts"
			fi

			checkopt ro $mntopts
			if [ "$option" != "ro" ]; then
				mntopts="ro,$mntopts"
			fi

			# Requesting logging on a read-only mount
			# causes errors to be displayed, so remove
			# "logging" from the list of options.
			checkopt logging $mntopts
			if [ "$option" = "logging" ]; then
				mntopts="$otherops"
			fi
		fi

		# In case of a manual restart of the service, mount
		# will emit messages if /usr is already mounted.
		# So redirect the output to /dev/null.
		$MOUNT -m -F $fstype -o $mntopts $new_special /usr \
>/dev/null 2>&1
		ret_val=$?
		if [ $ret_val -eq 0 ]; then
			usrmounted=1
		fi
	fi

	return $ret_val
}

# update system dump configuration
update_dumpconf()
{
	# Disable device-in-use checking (done in libdiskmgt).
	# Without disabling this check, the configuration of dump device
	# would fail as the device-in-use code incorrectly concludes that
	# the device is in use and hence prevents configuration of the dump
	# device.
	NOINUSE_CHECK=1
	export NOINUSE_CHECK

	DUMPISZFS=`$AWK -F"=" '/DUMPADM_DEVICE/ {print $2}' /etc/dumpadm.conf|$EGREP zvol`
	if [ -z "$DUMPISZFS" ]; then
		set -- `$DUMPADM -u 2>&1 | $EGREP 'cannot use /dev.* as dump device'`
		if [ -n "$4" ]; then
			newname=`$STMSBOOTUTIL -m $4`
			if [ $? -eq 0 ]; then
				if $DUMPADM -d $newname > /dev/msglog 2> /dev/console; then
					cecho "stmsboot: dump configuration \
					has been updated."
				else
					mpxio_error "failed to configure \
					the dump device.\nold \
					dump device name: $4"
					return 1
				fi
			fi
		fi
	else
		# make sure we can get to it, force zfs to load fully
		$LS $DUMPISZFS >>/dev/null 2>&1
		cecho "stmsboot: dump on ZFS, no dumpadm update required"
	fi
	return 0
}

# Update bootpath for x86 here when we are enabling mpxio on root
update_bootpath()
{
	cur_bootpath=`$STMSBOOTUTIL -b`
	if [ $? -ne 0 ]; then
		cecho "stmsboot: ERROR! Unable to retrieve bootpath property\n"
		exit 1
	fi

	# Since on x64 platforms the eeprom command doesn't update the
	# kernel, the file /boot/solaris/bootenv.rc and the kernel's
	# bootpath variable have a good chance of differing. We do some
	# extra handwaving to get the correct bootpath variable setting. 

	ONDISKVER=`$AWK '/bootpath/ {print $3}' /boot/solaris/bootenv.rc|\
		$SED -e"s,',,g"`
	if [ "$ONDISKVER" != "$cur_bootpath" ]; then
		cur_bootpath="$ONDISKVER"
	fi

	NEWBOOTPATH=""
	for path in $cur_bootpath; do
		mapped=`$STMSBOOTUTIL -p $path`
		if [ "$mapped" != "NOT_MAPPED" ]; then
			if [ "$mapped" != "$path" ]; then
				NEWBOOTPATH=`echo "$path " | \
				    $SED -e"s|$path|$mapped|"`" $NEWBOOTPATH"
			else
				NEWBOOTPATH="$NEWBOOTPATH $path"
			fi
		fi
	done
	# now strip off leading and trailing space chars
	new_bootpath=`echo $NEWBOOTPATH`
	$EEPROM bootpath="$new_bootpath"
	cecho "stmsboot: bootpath has been updated"
	cecho ""
}

# Now do the actual work
mpxio_main()
{
	# NOTE: If the first attempt to run the service has failed due to an
	# expected error, users should be able to manually rerun the service.
	#
	# First mount /usr read only. This must be done to run
	# utilities such as fsck and devfsadm.
	# In the case of a manual rerun of the service, mounting of /usr here
	# fails if /usr already happens to be mounted. It is better that we
	# do not mount /usr if already mounted, but there seems to be no
	# apparent way to check whether /usr is mounted or not as we mount
	# /usr without making an entry into /etc/mnttab. So instead of
	# explicitly checking for mount failures, we just do a sanity check
	# by looking for some file (in this case devfsadm) in /usr.
	#
	mpxio_mount_usr
	if [ ! -s $DEVFSADM ]; then
		mpxio_error "failed to mount the /usr filesystem."
		return
	fi

	if mpxio_mount_root; then
		# create /dev links
		cecho "stmsboot: configuring devices"
		$DEVFSADM

		if [ -n "$ISROOTDEVPATH" ]; then
			ISROOTDEV=`$STMSBOOTUTIL -o $ISROOTDEVPATH`
		fi

		# update /etc/vfstab to reflect device name changes
		$STMSBOOTUTIL -u >/dev/msglog 2>&1
		if [ $? -eq 0 ]; then
			$CP /etc/vfstab /etc/vfstab.old
			# handle active-active paths, where the probe order
			# for the hba reports a different path to what the
			# boot-device variable gives us
			if [ -n "$ISROOTDEV" ]; then
				ROOTDEVCHK=`grep $ISROOTDEV /etc/vfstab`
				if [ $? -ne 0 ]; then
					# we got a different path for root
					exec < $SAVEDIR/vfstab.new; readvfstab /
					FILEDEV=`$ECHO $special | \
					    $SED -e"s,/dev/dsk/,," -e"s,s[0-9]*,,"`
					$SED -e"s,$FILEDEV,$ISROOTDEV,g" < \
					    $SAVEDIR/vfstab.new > /etc/vfstab
				fi
			else
				$CP $SAVEDIR/vfstab.new /etc/vfstab
			fi
			$RM $SAVEDIR/vfstab.new
			cecho ""
			cecho "stmsboot: vfstab has been updated"
			
			update_dumpconf
			
			MACH=`$UNAME -p`
			if [ "$MACH" = "i386" ]; then
				# only update bootpath here for x86
				update_bootpath
			fi
			cecho "stmsboot: now regenerating boot archive"
			$BOOTADM update-archive
		else
			mpxio_error "failed to update /etc/vfstab."
		fi

		$SVCADM disable system/device/mpxio-upgrade
		
		if [ $usrmounted -eq 1 ]; then
			cecho "stmsboot: rebooting the system now."
			$REBOOT
		fi
	else
		mpxio_error "failed to mount the root filesystem."
	fi
}

mpxio_main
